#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>

#include "server_socket.c"

#define SERVERPORT 3333 
#define MAX_LINE 1024
#define FD_INIT_SIZE 128

// 用户态缓冲区，用于存储服务端需要发送的数据 
// 采用非阻塞写的话，数据一次性不一定发的完，
// 所以先存储在用户态缓冲区中
struct Buffer {
    int connect_fd;  // 客户端连接 socket 文件描述符
    char buffer[MAX_LINE];  //实际存储数据的缓冲区
	// buffer 中：
	// [0...readIndex) 区间数据已经写到客户端
	// [readIndex...writeIndex) 区间数据还未写到客户端
	// [writeIndex...MAX_LINE) 区间还未接收客户端的数据
    size_t writeIndex;    
    size_t readIndex;  
    int readable;           //是否可以读
};

// 初始化一个用户态缓冲区
struct Buffer *alloc_Buffer() {
    struct Buffer *buffer = malloc(sizeof(struct Buffer));
    if (!buffer)
        return NULL;
    buffer->connect_fd = 0;
    buffer->writeIndex = buffer->readIndex = buffer->readable = 0;
    return buffer;
}

// 释放指定缓冲区的内存
void free_Buffer(struct Buffer *buffer) {
    free(buffer);
}

// 字母大小写转换 
char process_char(char c) {
    if (c >= 'a' && c <= 'z') {
        return c - 32;
    } else if (c >= 'A' && c <= 'Z') {
        return c + 32;
    }
    return c;
}

int onSocketRead(int fd, struct Buffer *buffer) {
    char buf[1024];
    int i;
    ssize_t result;
    while (1) {
		// 非阻塞读取客户端数据到 buf 中
        result = read(fd, buf, sizeof(buf));
		// 没有读到数据，直接退出
        if (result <= 0)
            break;
		
		// 将读到的数据处理后，放到用户态缓冲区
        for (i = 0; i < result; ++i) {
            if (buffer->writeIndex < sizeof(buffer->buffer))
                buffer->buffer[buffer->writeIndex++] = process_char(buf[i]);
            if (buf[i] == '\n') {
                buffer->readable = 1;  //缓冲区可以读
            }
        }
    }
	
    if (result == 0) { // FIN 报文，调用方需要关闭当前连接 socket
        return 1;
    } else if (result < 0) {
        if (errno == EAGAIN) // 没有数据可读
            return 0;
		// 读的时候出错了
        return -1;
    }

    return 0;
}

int onSocketWrite(int fd, struct Buffer *buffer) {
    while (buffer->readIndex < buffer->writeIndex) {
        ssize_t result = write(fd, 
				buffer->buffer + buffer->readIndex, // 可写数据的起始位置
				buffer->writeIndex - buffer->readIndex); // 可写数据的长度
        
		if (result < 0) {
            if (errno == EAGAIN) // 发送内核态缓冲区没有空闲的地方
                return 0;
            return -1;
        }
		
		// 写完了 result 个字节
        buffer->readIndex += result;
    }
	
	// 没有可以写的数据了
    if (buffer->readIndex == buffer->writeIndex)
        buffer->readIndex = buffer->writeIndex = 0;

    buffer->readable = 0;

    return 0;
}

int main() {
    struct sockaddr_in cs;
    int a_size, i;
    int listen_socket_fd; 
	
	// 创建一个监听 socket
	listen_socket_fd = tcp_listen_socket(SERVERPORT);

	// 设置监听 socket 的 IO 为非阻塞 IO
	fcntl(listen_socket_fd, F_SETFL, O_NONBLOCK);
	
	// 初始化所有客户端连接 socket 缓存数据的缓冲区(用户态)
	struct Buffer *buffer[FD_INIT_SIZE];
    for (i = 0; i < FD_INIT_SIZE; ++i) {
        buffer[i] = alloc_Buffer();
    }	
	
	int retval;
	// 初始化 pollfd 数组
	struct pollfd event_set[FD_INIT_SIZE];
	// 数组的第一个元素是listen_fd，其余的用来记录将要连接的connect_fd
    event_set[0].fd = listen_socket_fd;
    event_set[0].events = POLLIN;

	// 用-1表示这个数组位置还没有被占用
	// 如果我们不想对某个 pollfd 结构进行事件检测，
	// 可以把它对应的 pollfd 结构的 fd 成员设置成一个负值。
	// 这样，poll 函数将忽略这样的 events 事件，检测完成以后，
	// 所对应的“returned events”的成员值也将设置为 0
    for (i = 1; i < FD_INIT_SIZE; i++) {
        event_set[i].fd = -1;
    }

	for (;;) {

		for (i = 1; i < FD_INIT_SIZE; i++) {
    	    if (buffer[i]->connect_fd > 0) {
				if (buffer[i]->readable) {
					// 如果用户态 buffer 中有可读数据，才注册可写事件
					event_set[i].events |= POLLOUT;
				} else if (event_set[i].events & POLLOUT) {
					// 如果没有可读数据，那么撤销可写事件的检测
					// POLLOUT：0000 0000 0000 0100
					event_set[i].events ^= POLLOUT;
				} 
			}
	    }

		
		// 通知内核，如果有我关心的 IO 事件发生的话，请通知我
		// 如果没有的话，我将一直阻塞下去，阻塞时放弃 CPU 执行权
		retval = poll(event_set, FD_INIT_SIZE, -1);
		if (retval < 0) {
			perror("select error");
		}
		
		printf("wakeup from poll\n");		
		
		// 判断监听 socket 是否有连接进来（可读事件发生）
		if (event_set[0].revents & POLLIN) {
			int fd = accept(listen_socket_fd, (struct sockaddr*)&cs, &a_size);
    		if(fd == -1) {
        		perror("accept");
        		exit(1);
    		} else if (fd > FD_INIT_SIZE) { 
				// 大于服务器同时支持的最大的 socket 连接
				perror("too many connections");
				close(fd);
			} else {
    			printf("accept success! fd = %d\n", fd);
				// 将当前连接进来的 socket 设置为非阻塞
				fcntl(fd, F_SETFL, O_NONBLOCK);
				for (i = 1; i < FD_INIT_SIZE; i++) {
                	if (event_set[i].fd < 0) {
                    	event_set[i].fd = fd;
                    	event_set[i].events = POLLIN;
						buffer[i]->connect_fd = fd;
                    	break;
                	}
            	}
			}
		}
		
		// 检测每个 fd，看看有没有感兴趣的 IO 事件发生
		for (i = 0; i < FD_INIT_SIZE; ++i) {
            int r = 0;
			int currfd = event_set[i].fd;
			// 监听 socket 关心的 IO 事件已经处理过了
            if (currfd == listen_socket_fd || currfd < 0)
                continue;
			
			// 处理当前连接 socket 可读事件
            if (event_set[i].revents & POLLIN) {
                //r = onSocketRead(currfd, buffer[i]);
                printf("get a readable event from fd = %d\n", currfd);
            }
			// 处理当前连接 socket 可写事件
            if (r == 0 && event_set[i].revents & POLLOUT) {
                r = onSocketWrite(currfd, buffer[i]);
            }
            if (r) { // 返回 1 的话，表示收到 FIN 报文，关闭连接
                // bug 修复，需要将当前 fd 从 events 中删除
				buffer[i]->connect_fd = 0;
				event_set[i].events = 0;
				close(currfd);
            }
        }
	}

	// 关闭服务端 server socket
    close(listen_socket_fd);
}
